from datetime import datetime
from flask import current_app, g, make_response, jsonify, request

from sqlalchemy.ext.declarative import declared_attr
from app.extensions import bcrypt, db, auth
from app.exception import APIException, DATABASE_ERROR
from app.utils import jwt_encode, jwt_decode


class CRUDMixin(object):

    @classmethod
    def create(cls, commit=True, **kwargs):
        """新增记录."""
        instance = cls(**kwargs)
        return instance.save(commit)

    def update(self, commit=True, **kwargs):
        """更新一条记录"""
        for attr, value in kwargs.items():
            if value is not None:
                setattr(self, attr, value)
        return commit and self.save() or self

    def save(self, commit=True):
        """保存记录."""
        db.session.add(self)
        try:
            db.session.flush()
        except Exception as e:
            db.session.rollback()
            current_app.logger.exception(e)
            raise APIException(DATABASE_ERROR)

        if commit:
            try:
                db.session.commit()
            except Exception as e:
                db.session.rollback()
                current_app.logger.exception(e)
                raise APIException(DATABASE_ERROR)
        return self

    def delete(self, commit=True):
        """删除记录."""
        db.session.delete(self)
        return commit and db.session.commit()


class TimestampMixin(object):
    @declared_attr
    def created(cls):
        return db.Column(db.DateTime, nullable=False, default=datetime.now, comment='创建时间')

    @declared_attr
    def updated(cls):
        return db.Column(db.DateTime, onupdate=datetime.now, comment='更新时间')


class Model(CRUDMixin, db.Model):
    """通用Model"""

    __abstract__ = True
    __table_args__ = {
        'mysql_engine': 'InnoDB',
        'mysql_charset': 'utf8mb4',
    }


class SurrogatePK(object):
    """A mixin that adds a surrogate integer 'primary key' column named ``id`` to any declarative-mapped class."""

    __table_args__ = {'extend_existing': True}

    id = db.Column(db.Integer, primary_key=True, comment='自增ID')


class AdminUser(SurrogatePK, Model, TimestampMixin):
    """管理员表"""
    __tablename__ = 't_admin_user'

    username = db.Column(db.String(20), nullable=False, comment='用户名')
    password = db.Column(db.String(255), nullable=False, comment='用户密码')
    real_name = db.Column(db.String(20), default='', comment='真实姓名')
    phone_number = db.Column(db.String(20), nullable=False, default='', comment='手机号')
    role = db.Column(db.String(255), default='user', comment='角色 admin:管理员 manager:门店兑换管理 ')
    branch_id = db.Column(db.Integer, default=0, comment='所属门店id 角色为manager是有效')
    login_time = db.Column(db.DateTime, comment='最后一次登录时间')
    login_ip = db.Column(db.String(30), comment='最后一次登录IP')
    locked = db.Column(db.Boolean, default=0, comment='用户锁定0：未锁定 1：锁定')
    deleted = db.Column(db.Boolean, default=0, comment='是否删除 1：删除')

    __comment__ = '管理员表'

    def __init__(self, username, phone_number, password=None, **kwargs):
        """Create instance."""
        db.Model.__init__(self, username=username, phone_number=phone_number, **kwargs)
        if password:
            self.set_password(password)
        else:
            self.password = None

    def set_password(self, password):
        self.password = bcrypt.generate_password_hash(password).decode('utf-8')

    def verify_password(self, password):
        return bcrypt.check_password_hash(self.password, password)

    def generate_auth_token(self):
        token = jwt_encode({'id': self.id, 'username': self.username})

        return token

    @staticmethod
    def verify_auth_token(token):
        try:
            obj = jwt_decode(token)
            data = obj['user']
            user = AdminUser.query.get(data['id'])
            return user
        except Exception as e:
            print(e)
            return None

    def __repr__(self):
        return '<User %r>' % self.username


@auth.verify_token
def verify_token(token):
    g.user = None
    if request.path.startswith('/api/admin/'):
        user = AdminUser.verify_auth_token(token)
    elif request.path.startswith('/api/wxapp/'):
        user = User.verify_auth_token(token)
    if user:
        g.user = user
        return True
    return False


@auth.error_handler
def unauthorized():
    return make_response(jsonify({'error': 'Unauthorized access'}), 401)


class Banner(SurrogatePK, Model, TimestampMixin):
    """Banner表"""
    __tablename__ = 't_banner'

    category = db.Column(db.String(32), nullable=False, default='none', comment='类型，link：跳转外部链接，默认为只展示')
    position = db.Column(db.SmallInteger, nullable=False, default=0, comment='广告位，0为首页')
    image_url = db.Column(db.String(255), nullable=False, default='', comment='图片链接')
    json_data = db.Column(db.String(255), nullable=False, default='', comment='json数据，内部跳转或外部链接，默认为空即不解析处理')
    start_time = db.Column(db.DateTime, comment='开始时间')
    end_time = db.Column(db.DateTime, comment='结束时间')
    weights = db.Column(db.Integer, nullable=False, default=0, comment='权值，值越大排在越前')
    deleted = db.Column(db.Boolean, default=0, comment='是否删除 1：删除')

    __comment__ = 'Banner表(轮播图表)'


class Branch(SurrogatePK, Model, TimestampMixin):
    """分店表"""
    __tablename__ = 't_branch'

    name = db.Column(db.String(255), nullable=False, default='', comment='分店名称')
    manager = db.Column(db.String(255), nullable=False, default='', comment='分店负责人')
    mobile = db.Column(db.String(11), nullable=False, default='', comment='负责人联系方式')
    address = db.Column(db.String(255), nullable=False, default='', comment='分店地址')
    enabled = db.Column(db.Boolean, default=1, comment='1: 启用，0: 禁用')

    admin = db.relationship(
        'AdminUser',
        primaryjoin='Branch.id == AdminUser.branch_id',
        foreign_keys='AdminUser.branch_id',
    )

    __comment__ = '分店表'


class Brand(SurrogatePK, Model, TimestampMixin):
    """兑换商品品牌表"""
    __tablename__ = 't_brand'

    name = db.Column(db.String(45), nullable=False, default='', comment='品牌名称')
    description = db.Column(db.String(255), nullable=False, default='', comment='品牌描述')
    deleted = db.Column(db.Boolean, default=0, comment='是否删除 1:删除')

    __comment__ = '兑换商品品牌表'


class Category(SurrogatePK, Model, TimestampMixin):
    """商品分类表"""
    __tablename__ = 't_category'

    name = db.Column(db.String(45), nullable=False, default='', comment='分类名称')
    pic_url = db.Column(db.String(255), nullable=False, default='', comment='分类封面')
    parent_id = db.Column(db.Integer, nullable=False, default=0, comment='上级分类ID，0表示一级分类')
    level = db.Column(db.Boolean, nullable=False, default=1, comment='分类等级1：一级分类 2：二级分类 3：三级分类，一共就3级分类')
    sort = db.Column(db.Integer, nullable=False, default=0, comment='排序')

    __comment__ = '商品分类表'


class Goods(SurrogatePK, Model, TimestampMixin):
    """商品表"""
    __tablename__ = 't_goods'

    name = db.Column(db.String(255), nullable=False, default='', comment='商品名称')
    sku = db.Column(db.String(255), nullable=False, default='', comment='商品SKU')
    brand_id = db.Column(db.Integer, nullable=False, default=0, comment='品牌ID')
    category_id = db.Column(db.Integer, nullable=False, default=0, comment='分类ID')
    detail = db.Column(db.Text, nullable=False, default='', comment='商品详情')
    weights = db.Column(db.Integer, nullable=False, default=0, comment='权值，值越大排在越前')
    is_listing = db.Column(db.Boolean, nullable=False, default=1, comment='是否上架 1: 上架  0:下架')
    recommended = db.Column(db.Boolean, nullable=False, default=0, comment='是否推荐 1：推荐  0: 不推荐')
    is_online = db.Column(db.Boolean, nullable=False, default=1, comment='是否允许线上兑换 1：允许  0: 不允许')
    pic_url = db.Column(db.String(128), nullable=False, default='', comment='主图url')
    exchange_price = db.Column(db.Integer, nullable=False, default=0, comment='兑换价格(娃娃个数)')
    stock = db.Column(db.Integer, nullable=False, default=0, comment='库存')
    exchange_num = db.Column(db.Integer, nullable=False, default=0, comment='已兑换数量')
    deleted = db.Column(db.Boolean, default=0, comment='是否删除 1:删除')

    brand = db.relationship(
        'Brand',
        primaryjoin='Goods.brand_id == Brand.id',
        uselist=False,
        foreign_keys='Brand.id',
    )
    category = db.relationship(
        'Category',
        primaryjoin='Goods.category_id == Category.id',
        uselist=False,
        foreign_keys='Category.id',
    )
    images = db.relationship(
        'GoodsPicture',
        primaryjoin='Goods.id == GoodsPicture.goods_id',
        foreign_keys='GoodsPicture.goods_id',
    )

    __comment__ = '商品表'


class GoodsPicture(SurrogatePK, Model, TimestampMixin):
    """商品图片表"""
    __tablename__ = 't_goods_picture'

    goods_id = db.Column(db.Integer, nullable=False, default=0, comment='商品ID')
    type = db.Column(db.Boolean, nullable=False, default=1, comment='图片类型 1：商品轮播图 2：商品详情图')
    width = db.Column(db.Integer, nullable=False, default=0, comment='图片宽度')
    height = db.Column(db.Integer, nullable=False, default=0, comment='图片高度')
    url = db.Column(db.String(128), nullable=False, default='', comment='图片url')

    __comment__ = '商品图片表'


class Order(SurrogatePK, Model, TimestampMixin):
    """订单表"""
    __tablename__ = 't_order'

    order_no = db.Column(db.String(255), nullable=False, default='', comment='订单号')
    goods_id = db.Column(db.Integer, nullable=False, default=0, comment='商品ID')
    user_id = db.Column(db.Integer, nullable=False, default=0, comment='用户ID')
    branch_id = db.Column(db.Integer, nullable=False, default=0, comment='分店ID')
    order_time = db.Column(db.DateTime, comment='下单时间')
    exchange_type = db.Column(db.SmallInteger, nullable=False, default=1, comment='兑换方式(1：线上 0：线下 )')
    status = db.Column(db.SmallInteger, nullable=False, default=1, comment='订单状态(0:待处理 1:待发货 2:已发货 3:已完成 4: 已拒绝)')
    amount = db.Column(db.Integer, nullable=False, default=1, comment='订单金额(娃娃个数)')
    address_id = db.Column(db.Integer, nullable=False, default=0, comment='收货地址ID')
    courier_company = db.Column(db.String(255), default='', comment='快递公司')
    courier_no = db.Column(db.String(255), default='', comment='快递单号')
    remark = db.Column(db.String(255), nullable=False, default='', comment='备注')
    # handle_time = db.Column(db.DateTime, comment='处理时间：包括发货时间、拒绝时间')
    # receive_time = db.Column(db.DateTime, comment='完成时间')

    user = db.relationship(
        'User',
        primaryjoin='Order.user_id == User.id',
        uselist=False,
        foreign_keys='User.id',
    )

    goods = db.relationship(
        'Goods',
        primaryjoin='Order.goods_id == Goods.id',
        uselist=False,
        foreign_keys='Goods.id',
    )

    branch = db.relationship(
        'Branch',
        primaryjoin='Order.branch_id == Branch.id',
        uselist=False,
        foreign_keys='Branch.id',
    )

    address = db.relationship(
        'UserAddress',
        primaryjoin='Order.address_id == UserAddress.id',
        uselist=False,
        foreign_keys='UserAddress.id',
    )

    __comment__ = '订单表'


class User(SurrogatePK, Model, TimestampMixin):
    """ 用户表 """
    __tablename__ = 't_user'

    uid = db.Column(db.String(10), nullable=False, default=0, comment='展示给用户看的唯一ID，六位数字加字母的字符串')
    nickname = db.Column(db.String(255), nullable=False, default='', comment='用户昵称')
    avatar = db.Column(db.String(255), nullable=False, default='', comment='用户头像')
    doll = db.Column(db.Integer, nullable=False, default=0, comment='娃娃个数')
    branch_id = db.Column(db.Integer, nullable=False, default=0, comment='用户所属分店ID')
    status = db.Column(db.Boolean, nullable=False, default=1, comment='是否禁用   1：启用  0: 禁用')
    gender = db.Column(db.SmallInteger, nullable=False, default=0, comment='用户的性别，值为1时是男性，值为2时是女性，值为0时是未知')
    city = db.Column(db.String(255), nullable=False, default='', comment='用户所在城市')
    province = db.Column(db.String(255), nullable=False, default='', comment='用户所在省份')
    country = db.Column(db.String(255), nullable=False, default='', comment='用户所在国家')

    branch = db.relationship(
        'Branch',
        primaryjoin='Branch.id == User.branch_id',
        uselist=False,
        foreign_keys='Branch.id',
    )

    __comment__ = "用户表"

    def generate_auth_token(self):
        token = jwt_encode({'id': self.id, 'nickname': self.nickname})

        return token

    @staticmethod
    def verify_auth_token(token):
        try:
            obj = jwt_decode(token)
            data = obj['user']
            user = User.query.get(data['id'])
            return user
        except Exception as e:
            print(e)
            return None

    def __repr__(self):
        return '<User %r>' % self.nickname


class UserAddress(SurrogatePK, Model, TimestampMixin):
    """ 用户收件地址表 """
    __tablename__ = 't_user_address'

    user_id = db.Column(db.Integer, nullable=False, default=0, comment='用户ID')
    name = db.Column(db.String(45), nullable=False, default='', comment='收件人姓名')
    phone = db.Column(db.String(20), nullable=False, default='', comment='收件人电话')
    address = db.Column(db.String(255), nullable=False, default='', comment='收件人地址')
    province = db.Column(db.String(45), default='', comment='省')
    city = db.Column(db.String(45), default='', comment='城市-地级市')
    county = db.Column(db.String(45), default='', comment='县区')
    area_code = db.Column(db.String(45), default='', comment='地区编码')
    alias = db.Column(db.String(45), default='', comment='地址别名')
    deleted = db.Column(db.Boolean, nullable=False, default=0, comment='是否删除（0：未删除，1：删除）')
    is_default = db.Column(db.Boolean, default=0, comment='是否默认（1：默认）')

    __comment__ = "用户收件地址表"


class UserLogin(SurrogatePK, Model, TimestampMixin):
    """ 用户登录表 """
    __tablename__ = 't_user_login'

    user_id = db.Column(db.Integer, nullable=False, comment='用户id')
    identity_type = db.Column(db.String(128), nullable=False, default='', comment='登录类型："mobile"，"weixin"，"QQ"，"weibo"')
    identifier = db.Column(db.String(255), nullable=False, default='', comment='身份唯一标识：第三方openid或手机号')
    credential = db.Column(db.String(255), nullable=False, comment='授权凭证（比如密码 第三方登录的token等）')
    enabled = db.Column(db.Integer, nullable=False, default=1, comment='1: 启用，2: 禁用')

    __comment__ = "用户登录表"


class UserRecord(SurrogatePK, Model, TimestampMixin):
    """ 用户娃娃变化记录表 """
    __tablename__ = 't_user_record'

    user_id = db.Column(db.Integer, nullable=False, comment='用户id')
    type = db.Column(db.Integer, nullable=False, default=0, comment='变化类型： 1：充值 2：线上兑换 3：线下兑换 4：赞助')
    doll = db.Column(db.Integer, nullable=False, default=0, comment='正负娃娃个数 type=3 doll为正表示订单拒绝后退回娃娃')
    operate_id = db.Column(db.Integer, nullable=False, default=0, comment='操作用户id，type位1和3时表示门店管理员id')

    __comment__ = "用户娃娃变化记录表"


class UserSponsorRecord(SurrogatePK, Model, TimestampMixin):
    """ 娃娃赠送记录表 """
    __tablename__ = 't_user_sponsor_record'

    from_id = db.Column(db.Integer, nullable=False, comment='赞助用户id')
    from_name = db.Column(db.String(255), nullable=False, comment='赞助用户昵称')
    to_id = db.Column(db.Integer, nullable=False, comment='被赞助用户id')
    to_name = db.Column(db.String(255), nullable=False, comment='被赞助用户昵称')
    doll = db.Column(db.Integer, nullable=False, default=0, comment='赞助娃娃个数')

    __comment__ = "娃娃赠送记录表"


class Explain(SurrogatePK, Model, TimestampMixin):
    """兑换说明表"""
    __tablename__ = 't_explain'

    image_url = db.Column(db.String(255), nullable=False, default='', comment='图片链接')
    json_data = db.Column(db.String(255), nullable=False, default='', comment='json数据，内部跳转或外部链接，默认为空即不解析处理')

    __comment__ = '兑换说明表'